/*
 * Copyright (c) 2019 , Healthlink All Rights Reserved.
 */

package com.mayday.system.service.impl;

import com.mayday.system.domain.FlowDef;
import com.mayday.system.domain.MidTask;
import org.activiti.bpmn.BpmnAutoLayout;
import org.activiti.bpmn.model.*;
import org.springframework.util.StringUtils;

import org.activiti.bpmn.model.Process;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 构建流程图
 *
 * @author lxq
 * @version 1.0
 * @date 2020/4/22 10:12
 * @since JDK 1.8+
 */
public class BpmnBuilder {

    private static final String FLOW_START = "start";
    private static final String FLOW_END = "end";
    private static final String FLOW_USER_TASK = "usertask";
    private static final String FLOW_USER_TASK1 = "usertask1";
    private static final String FLOW_USER_TASK2 = "usertask2";
    private static final String FLOW_EXCLUSIVEGATEWAY = "exclusiveGateway";
    private static final Pattern PATTERN_REGULAR = Pattern.compile("[^0-9]");

    /**
     * 构建图
     * @param flowDef 画图参数
     * @return BpmnModel
     */
    BpmnModel build(FlowDef flowDef) {
        List<MidTask> midTasks = flowDef.getMidTask();
        BpmnModel model = new BpmnModel();
        Process process = new Process();
        model.addProcess(process);
        process.setId(flowDef.getFlowKey());
        process.setName(flowDef.getFlowName());
        process.addFlowElement(createStartEvent());
        process.addFlowElement(createEndEvent());

        for (int i = 0; i < midTasks.size(); i++) {
            String userTaskId = FLOW_USER_TASK + (i + 1);
            MidTask midTask = midTasks.get(i);

            if (i == 0) {
                UserTask userTask = new UserTask();
                userTask.setId(userTaskId);
                userTask.setName(midTask.getNodeName());
                userTask.setCandidateGroups(midTask.getRoles());
                userTask.setCandidateUsers(midTask.getStaffs());
                process.addFlowElement(userTask);

                SequenceFlow sequenceFlow = createSequenceFlow(FLOW_START, userTaskId, null, null);
                process.addFlowElement(sequenceFlow);

                SequenceFlow sequenceFlow2 = createSequenceFlow(userTaskId, FLOW_USER_TASK2, null, null);
                process.addFlowElement(sequenceFlow2);
            } else {
                String exclusiveGatewayId = FLOW_EXCLUSIVEGATEWAY + i;
                UserTask userTask = new UserTask();
                userTask.setId(userTaskId);
                userTask.setName(midTask.getNodeName());
                userTask.setCandidateGroups(midTask.getRoles());
                userTask.setCandidateUsers(midTask.getStaffs());
                process.addFlowElement(userTask);

                ExclusiveGateway exclusiveGateway = creatExclusiveGateway(exclusiveGatewayId);
                process.addFlowElement(exclusiveGateway);

                process.addFlowElement(createSequenceFlow(userTaskId, exclusiveGatewayId, null, null));

                SequenceFlow sequenceFlowNo = createSequenceFlow(exclusiveGatewayId, FLOW_USER_TASK1, "${!pass}", "不通过");
                process.addFlowElement(sequenceFlowNo);

                String nextTask = i == midTasks.size() - 1 ? FLOW_END : FLOW_USER_TASK + (i + 2);
                process.addFlowElement(createSequenceFlow(exclusiveGatewayId, nextTask, "${pass}", "通过"));
            }
        }

        new BpmnAutoLayout(model).execute();

        //TODO 流程图位置修改
        model.getLocationMap();
        GraphicInfo graphicInfoStart = model.getLocationMap().get(FLOW_START);
        model.getLocationMap().forEach((s, graphicInfo) -> {
            if (graphicInfo.getElement() instanceof ExclusiveGateway) {
                graphicInfo.setY(graphicInfoStart.getY() - 5);
            } else if (graphicInfo.getElement() instanceof UserTask) {
                graphicInfo.setY(graphicInfoStart.getY() - 15);
            } else if (graphicInfo.getElement() instanceof Event && graphicInfo != graphicInfoStart) {
                graphicInfo.setY(graphicInfoStart.getY());
            }
        });

        Map<String, List<GraphicInfo>> map = new HashMap<>(6);
        int i=3;
        //TODO 流程图位置修改
        model.getFlowLocationMap().forEach((s, graphicInfos) -> {
            SequenceFlow sequenceFlow = (SequenceFlow) graphicInfos.get(0).getElement();
            String target = sequenceFlow.getTargetRef();
            String source = sequenceFlow.getSourceRef();
            GraphicInfo graphicInfoT = model.getLocationMap().get(target);
            GraphicInfo graphicInfoS = model.getLocationMap().get(source);

            double xTarget = graphicInfoT.getX() + (graphicInfoT.getWidth() / 2);
            double yTarget = graphicInfoT.getY() + (graphicInfoT.getHeight());

            double xSource = graphicInfoS.getX() + (graphicInfoS.getWidth() / 2);
            double ySource = graphicInfoS.getY() + (graphicInfoS.getHeight());
            if (sequenceFlow.getTargetRef().equals(FLOW_USER_TASK1) && !sequenceFlow.getSourceRef().equals(FLOW_START)) {
              int k= Integer.parseInt(PATTERN_REGULAR.matcher(sequenceFlow.getSourceRef()).replaceAll("").trim())+i ;
                graphicInfos.get(0).setX(xSource);
                graphicInfos.get(0).setY(ySource);

                graphicInfos.get(1).setX(xSource);
                graphicInfos.get(1).setY(ySource + k *10);

                graphicInfos.get(2).setX(xTarget);
                graphicInfos.get(2).setY(ySource + k *10);

                graphicInfos.get(3).setX(xTarget);
                graphicInfos.get(3).setY(yTarget);
            } else {
                graphicInfos.get(0).setX(graphicInfoS.getX() + graphicInfoS.getWidth());
                graphicInfos.get(0).setY(graphicInfoS.getY() + (graphicInfoS.getHeight() / 2));

                graphicInfos.get(1).setX(graphicInfoT.getX());
                graphicInfos.get(1).setY(graphicInfoT.getY() + (graphicInfoT.getHeight() / 2));
                map.put(s, graphicInfos.subList(0, 2));
            }
        });

        model.getFlowLocationMap().putAll(map);
        return model;
    }


    /**
     * start userTask
     * @return StartEvent
     */
    private StartEvent createStartEvent() {
        StartEvent startEvent = new StartEvent();
        startEvent.setId(FLOW_START);
        return startEvent;
    }

    /**
     * end userTask
     * @return EndEvent
     */
    private EndEvent createEndEvent() {
        EndEvent endEvent = new EndEvent();
        endEvent.setId(FLOW_END);
        return endEvent;
    }

    /**
     * ExclusiveGateway
     * @param id
     * @return ExclusiveGateway
     */
    private ExclusiveGateway creatExclusiveGateway(String id) {
        ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
        exclusiveGateway.setId(id);
        return exclusiveGateway;
    }

    /**
     * SequenceFlow
     * @param from
     * @param to
     * @param conditionExpression
     * @param conditionExpressionDesc
     * @return SequenceFlow
     */
    private SequenceFlow createSequenceFlow(String from, String to, String conditionExpression, String conditionExpressionDesc) {
        SequenceFlow flow = new SequenceFlow();
        flow.setTargetRef(to);
        flow.setSourceRef(from);

        if (!StringUtils.isEmpty(conditionExpression)) {
            flow.setConditionExpression(conditionExpression);
            flow.setName(conditionExpressionDesc);
        }
        return flow;
    }

}
